﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.Collections;
using CDSB.Indicators.Model;
using CDSB.Indicators.Comum;
using CDSB.Indicators.Communication.Translator;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace CDSB.Indicators.Communication
{
    public class TFSAccess:IDisposable
    {
        private TeamFoundationServer TFServer;
        private VersionControlServer TFSVersionControl;
        private WorkItemStore TFSStore; 
        public TFSAccess(string serverName)
        {
            this.TFServer = TeamFoundationServerFactory.GetServer(serverName);
            this.TFSVersionControl = (VersionControlServer)TFServer.GetService(typeof(VersionControlServer));
            this.TFSStore = (WorkItemStore)TFServer.GetService(typeof(WorkItemStore));
        }

        /// <summary>
        /// Executa uma WIQL(Work Item Query Language) e retorna uma WorkItemCollection
        /// </summary>
        /// <param name="wiql">string</param>
        /// <returns>WorkItemCollection</returns>
        private WorkItemCollection ExecuteQuery(string wiql)
        {
            WorkItemCollection WorkitemsCollection = TFSStore.Query(wiql);

            return WorkitemsCollection;
        }

        /// <summary>
        /// Retorna um array com a lista de TeamProjects do Servidor
        /// </summary>
        /// <returns>TeamProject[]</returns>
        private TeamProject[] GetTeamProjectList() 
        {
            return TFSVersionControl.GetAllTeamProjects(true);
        }

        /// <summary>
        /// Retorna o histórico do Branch informado no parâmetro pathBranch
        /// </summary>
        /// <param name="pathBranch">Caminho do Branch</param>
        /// <returns>IEnumerable</returns>
        private IEnumerable GetBranchHistory(string pathBranch)
        {
            IEnumerable BranchHistory = TFSVersionControl.QueryHistory(pathBranch, VersionSpec.Latest, 0, RecursionType.Full, null, VersionSpec.ParseSingleSpec("1", null), VersionSpec.Latest, Int32.MaxValue, true, true);
            
            return BranchHistory;
        }

        /// <summary>
        /// Retorna um IEnumerable com todos os Changesets do Branch informado no parâmetro pathBranch
        /// </summary>
        /// <param name="pathBranch"></param>
        /// <returns>IEnumerable of Changeset</returns>
        private IEnumerable<Changeset> GetChangesetlist(string pathBranch)
        {
            IEnumerable<Changeset> BranchHistory = (IEnumerable<Changeset>)this.GetBranchHistory(pathBranch);

            return BranchHistory;
        }

        /// <summary>
        /// Retorna uma lista com os todos os ChangeSets do Branch informado no parâmetro pathBranch 
        /// criados pela equipe da CDSB no período informado nos parâmetros start e finish Date.
        /// </summary>
        /// <param name="pathBranch">Caminho do Branch</param>
        /// <param name="startDate">Data inicial</param>
        /// <param name="finishDate">Data Final</param>
        /// <returns>List of Changest</returns>
        private List<Changeset> GetChangesetlist(string pathBranch, DateTime startDate, DateTime finishDate)
        {
            IEnumerable BranchHistory = this.GetBranchHistory(pathBranch);

            List<Changeset> ChangesetList = new List<Changeset>();

            foreach (Changeset changeset in BranchHistory) 
            {
                string owner = changeset.Owner.Substring(changeset.Owner.IndexOf(@"\")+1).ToLower();

                if (changeset.CreationDate.Date <= finishDate && changeset.CreationDate.Date >= startDate && ConfigAccess.GetloginsCDSB().Contains(owner))
                {
                    ChangesetList.Add(changeset);
                }
                else if (changeset.CreationDate < startDate)
                {
                    break;
                }
            }

            BranchHistory = null;

            return ChangesetList;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="finishDate"></param>
        /// <returns></returns>
        public List<CDSBChangeset> GetAllCDSBChangeSetForDate(DateTime startDate, DateTime finishDate) 
        {
            List<CDSBChangeset> allChangeSets = new List<CDSBChangeset>();

            foreach (TeamProject teamProject in this.GetTeamProjectList())
            {
                foreach (Changeset changeset in this.GetChangesetlist(teamProject.ServerItem, startDate, finishDate))
                {
                    int countMerge = 0;
                    CDSBChangeset cdsbChangeset = new CDSBChangeset();
                    countMerge = (from change in changeset.Changes where change.ChangeType.ToString().Contains("Merge") select change).Count();

                    cdsbChangeset.ChangesetId = changeset.ChangesetId;
                    cdsbChangeset.ChangeOwner = changeset.Owner;
                    cdsbChangeset.ChangeType = countMerge > 0 ? Enum.ChangeType.Merge : Enum.ChangeType.Checkin;

                    allChangeSets.Add(cdsbChangeset);
                } 
            }

            return allChangeSets;
        }

        /// <summary>
        /// Retorna as Change Requests abertas no período Informado nos parâmetros start e finish Date.
        /// </summary>
        /// <param name="startDate">Inicio do Período</param>
        /// <param name="finishDate">Fim do Período</param>
        /// <returns></returns>
        public List<CDSBWorkItem> GetChangeRequestForDate(DateTime startDate, DateTime finishDate) 
        {
            List<CDSBWorkItem> CDSBChangeRequests = new List<CDSBWorkItem>();
            
            string sqlql = @"SELECT [System.Id] FROM WorkItems 
                                WHERE 
                                         [System.WorkItemType] = 'Change Request'
                                AND      [System.TeamProject] <> 'TEST'  
                                AND      [System.TeamProject] <> 'SINAINFR'
                                AND      [System.Reason] <> 'Cancelada. Analise informações adicionadas ao histórico.'  
                                AND      [System.Reason] <> 'Requisição inviabilizada. Analise informações adicionadas ao histórico.'  
                                AND      [System.Reason] <> 'Recusada. Analise informações adicionadas ao histórico.'  
                                AND      [System.Reason] <> 'Faltam informações, analise informações adicionadas ao histórico.'  
                                AND      [System.Reason] <> 'Revisar novamente, analise informações adicionadas ao histórico.'  
                                AND      [System.CreatedDate] >= '" + startDate.ToTeamSystemDateFormat() + @"'  
                                AND      [System.CreatedDate] <= '" + finishDate.ToTeamSystemDateFormat() + @"' 
                                ORDER BY [System.Id]";

            foreach (WorkItem wItem in this.ExecuteQuery(sqlql))
            {
                CDSBWorkItem CDSBWitem = new CDSBWorkItem();

                CDSBWitem = wItem.TranlateToCDSBWorkItem();

                CDSBChangeRequests.Add(CDSBWitem);
            }

            return CDSBChangeRequests;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="finishDate"></param>
        /// <returns></returns>
        private WorkItemCollection GetAllWorkItemsForDate(DateTime startDate, DateTime finishDate) 
        {
            string sqlql = @"SELECT 
	                            [System.ID]
                            FROM 
	                            WorkItems 
                            WHERE 
                                    [System.TeamProject] <> 'TEST'
                            AND     [Bovespa.SINACOR.TerminoRealizado] >= '" + startDate.ToTeamSystemDateFormat() + @"'
                            AND  	[Bovespa.SINACOR.TerminoRealizado] <= '" + finishDate.ToTeamSystemDateFormat() + @"'
                            AND     ([System.WorkItemType] = 'Bug'  OR  [System.WorkItemType] = 'TaskDesenv' or [System.WorkItemType] = 'BugInterno')
                            ORDER BY 
                                    [System.Id]";

            return this.ExecuteQuery(sqlql);
        }

        /// <summary>
        /// Retorna uma Classe com a coleção de tasks e bugs tratados pela CDSB durante o período informado nos parâmentos start e finish date.
        /// </summary>
        /// <param name="startDate">Inicio do Período</param>
        /// <param name="finishDate">Fim do Período</param>
        /// <returns></returns>
        public List<CDSBScenario> GetScenarioCollectionForDate(DateTime startDate, DateTime finishDate)
        {
            List<CDSBScenario> ScenarioCollection = new List<CDSBScenario>();

            // Consulta todas as tasks que retornaram code-review no período
            WorkItemCollection ReviewedList = this.GetTasksThatWereReviewed(startDate, finishDate);

            // Consulta todas as tasks que retornaram code-review no período
            WorkItemCollection CodeReviewList = this.GetTasksThatReturnedCodeReviewForDate(startDate, finishDate);

            ScenarioCollection.Add(new CDSBScenario() { ID = -1, Title = "Agrupador de Task/Bug sem Cenário Linkado" });
            ScenarioCollection.Add(new CDSBScenario() { ID =  0, Title = "Bugs de Produção", Area = "Suporte 3º Nível", Project = "Suporte 3º Nível" });

            // Para cada bug/taskdesenv desenvolvida no perído
            foreach (WorkItem wItem in this.GetAllWorkItemsForDate(startDate, finishDate))
            {
                CDSBScenario Scenario = null;
                WorkItem TFSScenario = null;
                int idScenario;

                switch (wItem.Type.Name.ToLower())
                {
                    // Se for task
                    case "taskdesenv":

                        if (wItem.State.ToLower() == "concluida" || wItem.State.ToLower() == "cancelada")
                        {
                            // Instancia um novo objseto para a task e a traduz.
                            CDSBTaskDesenv TaskDesenv = new CDSBTaskDesenv();
                            TaskDesenv = wItem.TranlateToCDSBTaskDesenv();

                            // Verifica se a task está na lista de tarefas que retornaram code review
                            if (ReviewedList.ContainsId(wItem.Id) && !CodeReviewList.ContainsId(wItem.Id))
                            {
                                TaskDesenv.IsCodeApproved = true;
                            }
                            else if (CodeReviewList.ContainsId(wItem.Id))
                            {
                                TaskDesenv.IsCodeApproved = false;
                            }

                            // Pelos links encontra o cenário da task
                            TFSScenario = this.FindScenarioOnLinks(wItem.Links);

                            // Se encontrou um cenário nos links, atribui o id do mesmo à variável idSecenario, 
                            // Senão atribui -1 para incluir essa task no scenario agrupador de task sem link para cenário
                            idScenario = TFSScenario != null ? TFSScenario.Id : -1;

                            // Verifica se o id do cenário linkado na task está na coleção de cenários CDSB
                            Scenario = ScenarioCollection.FindScenario(idScenario);

                            // Se não encontrar o cenário na coleção da CDSB, cria um novo objeto para esse cenário
                            // Adiciona a task na coleção de task do novo cenário e add o cenário recem criado na
                            // coleção de cenários CDSB
                            if (Scenario == null)
                            {
                                Scenario = TFSScenario.TranlateToCDSBScenario();
                                Scenario.Project = ConfigAccess.GetCDSBProjects().FindProject(TaskDesenv.Area).ProjectName;
                                Scenario.TasksList.Add(TaskDesenv);
                                ScenarioCollection.Add(Scenario);
                            }
                            // Se encontrar o cenário add mais uma task na coleção de tasks do cenário
                            else
                            {
                                Scenario.TasksList.Add(TaskDesenv);
                            }
                        }
                        break;
                    
                    // Se for Bug
                    case "buginterno":
                    case "bug":

                        // Instancia um novo objseto para o bug e o traduz.
                        CDSBBug Bug = new CDSBBug();
                        Bug = wItem.TranlateToCDSBBug();

                        //Se for bug de produção
                        if (Bug.Origem == Enum.TipoOrigem.Producao)
                        {
                            // Id do cenário recebe 0 para o bug ir para o agrupador de bugs de produção.
                            idScenario = 0;
                        }
                        else 
                        {
                            // Pelos links encontra o cenário do bug
                            TFSScenario = this.FindScenarioOnLinks(wItem.Links);

                            // Se encontrou um cenário nos links, atribui o id do mesmo à variável idSecenario, 
                            // Senão atribui -1 para incluir o bug no scenario agrupador de bugs sem link para cenário
                            idScenario = TFSScenario != null ? TFSScenario.Id : -1;
                        }

                        // Verifica se o id do cenário linkado na task está na coleção de cenários CDSB
                        Scenario = ScenarioCollection.FindScenario(idScenario);

                        // Se não encontrar o cenário na coleção da CDSB, cria um novo objeto para esse cenário
                        // Adiciona o bug na coleção de bugs do novo cenário e add o cenário recem criado na
                        // coleção de cenários CDSB
                        if (Scenario == null)
                        {
                            Scenario = TFSScenario.TranlateToCDSBScenario();
                            Scenario.Project = ConfigAccess.GetCDSBProjects().FindProject(Bug.Area).ProjectName;
                            Scenario.BugsList.Add(Bug);
                            ScenarioCollection.Add(Scenario);
                        }
                        // Se encontrar o cenário add mais um bug na coleção de bugs do cenário
                        else 
                        {
                            Scenario.BugsList.Add(Bug);
                        }

                        break;
                }
            }

            CodeReviewList = null;
            
            this.SetLastTasksInOldScenarios(ScenarioCollection);

            return ScenarioCollection;
        }

        /// <summary>
        /// Retorna uma coleção de tasksdesenv que retornaram code review no período
        /// </summary>
        /// <param name="startDate">Inicio do Período</param>
        /// <param name="finishDate">Fim do Período</param>
        /// <returns></returns>
        public WorkItemCollection GetTasksThatReturnedCodeReviewForDate(DateTime startDate, DateTime finishDate) 
        {
            string sqlql = @"SELECT
                                [System.Id]
                            FROM 
                                WorkItems 
                            WHERE 
                                 [System.TeamProject] <> 'TEST'  
                            AND      [System.TeamProject] <> 'SINAINFR'
                            AND  [System.WorkItemType] = 'TaskDesenv' 
                            AND  [System.State] EVER 'Implementando Code Review'  
                            AND  [Bovespa.SINACOR.TerminoRealizado] >= '" + startDate.ToTeamSystemDateFormat() + @"'  
                            AND  [Bovespa.SINACOR.TerminoRealizado] <= '" + finishDate.ToTeamSystemDateFormat() + @"' 
                            ORDER BY [System.Id]";
            
            List<CDSBTaskDesenv> Tasks = new List<CDSBTaskDesenv>();

            return this.ExecuteQuery(sqlql);
        }

        /// <summary>
        /// Retorna uma coleção de tasksdesenv que retornaram code review no período
        /// </summary>
        /// <param name="startDate">Inicio do Período</param>
        /// <param name="finishDate">Fim do Período</param>
        /// <returns></returns>
        public WorkItemCollection GetTasksThatWereReviewed(DateTime startDate, DateTime finishDate)
        {
            string sqlql = @"SELECT
                                [System.Id]
                            FROM 
                                WorkItems 
                            WHERE 
                                 [System.TeamProject] <> 'TEST'  
                            AND  [System.TeamProject] <> 'SINAINFR'
                            AND  [System.WorkItemType] = 'TaskDesenv' 
                            AND  [System.State] EVER 'Executando Code Review'  
                            AND  [Bovespa.SINACOR.TerminoRealizado] >= '" + startDate.ToTeamSystemDateFormat() + @"'  
                            AND  [Bovespa.SINACOR.TerminoRealizado] <= '" + finishDate.ToTeamSystemDateFormat() + @"' 
                            ORDER BY [System.Id]";

            List<CDSBTaskDesenv> Tasks = new List<CDSBTaskDesenv>();

            return this.ExecuteQuery(sqlql);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="links"></param>
        /// <returns></returns>
        public WorkItem FindScenarioOnLinks(LinkCollection links)
        {
            WorkItem Scenario = null;
            string idsLinks = string.Empty;

            foreach (Link link in links)
            {
                if (link.BaseType == BaseLinkType.RelatedLink)
                {
                    idsLinks += string.IsNullOrEmpty(idsLinks) ? ((RelatedLink)link).RelatedWorkItemId.ToString() : "," + ((RelatedLink)link).RelatedWorkItemId.ToString();
                }
            }

            if (idsLinks != string.Empty)
            {

                string wiql = string.Format("SELECT [System.Id] FROM WorkItems WHERE [System.Id] in ({0})", idsLinks);

                WorkItemCollection WiColl = this.ExecuteQuery(wiql);

                for (int item = WiColl.Count; item > 0; item--)
                {
                    if (WiColl[item - 1].Type.Name.ToLower() == "scenario")
                    {
                        Scenario = WiColl[item - 1];
                        break;
                    }
                }
            }
            else 
            {
                Scenario = null;
            }

            return Scenario;
        }

        public CDSBTaskDesenv FindLastTaskOnLink(LinkCollection links) 
        {
            CDSBTaskDesenv OldTask = null;
            string idsLinks = string.Empty;

            foreach (Link link in links)
            {
                if (link.BaseType == BaseLinkType.RelatedLink)
                {
                    idsLinks += string.IsNullOrEmpty(idsLinks) ? ((RelatedLink)link).RelatedWorkItemId.ToString() : "," + ((RelatedLink)link).RelatedWorkItemId.ToString();
                }
            }

            if (idsLinks != string.Empty)
            {
                string wiql = string.Format("SELECT [System.Id] FROM WorkItems WHERE [System.Id] in ({0})", idsLinks);

                WorkItemCollection WiColl = this.ExecuteQuery(wiql);

                foreach (WorkItem task in WiColl)
                {
                    if (task.Type.Name.ToLower() == "taskdesenv")
                    {
                        DateTime  LastDate = task.Fields["Termino Realizado"].Value != null ? (DateTime)task.Fields["Termino Realizado"].Value : DateTime.MinValue;

                        if (OldTask == null || OldTask.RealizedFinishDate.Date < LastDate.Date) 
                        {
                            OldTask = task.TranlateToCDSBTaskDesenv();
                            OldTask.IsOldTask = true;
                        }
                    }
                }
            }

            return OldTask;
        }

        public void SetLastTasksInOldScenarios(IEnumerable<CDSBScenario> scenarioCollection) 
        {
            string IdsOldScenarios = string.Empty;
            string wiql = "SELECT [System.Id] FROM WorkItems WHERE [System.Id] in ({0})";

            IEnumerable<CDSBScenario> OldScenarios = from cenario in scenarioCollection where cenario.TasksList.Count == 0 select cenario;

            foreach (CDSBScenario scenario in OldScenarios)
            {
                IdsOldScenarios += IdsOldScenarios.Length == 0 ? scenario.ID.ToString() : "," + scenario.ID.ToString();
            }

            foreach (WorkItem Task in this.ExecuteQuery(string.Format(wiql,IdsOldScenarios)))
            {
                CDSBTaskDesenv LastTask = FindLastTaskOnLink(Task.Links);

                if (LastTask != null)
                {
                    scenarioCollection.FindScenario(Task.Id).TasksList.Add(LastTask);                    
                }
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            this.TFServer.Dispose();
            this.TFSVersionControl = null;
        }

        #endregion
    }
}
